home *** CD-ROM | disk | FTP | other *** search
/ CD ROM Paradise Collection 4 / CD ROM Paradise Collection 4 1995 Nov.iso / edit / aaem95ma.zip / CC.CC < prev    next >
C/C++ Source or Header  |  1995-03-06  |  43KB  |  847 lines

  1. /* This file is CC.CC */
  2. #include "em.h"
  3. //#include <stdio.h>
  4. //FILE*debug;
  5. //#define DB 4
  6. /*-----*/
  7. #ifdef DB
  8. static int depth(){long j,i;for(i=long(&i+2),j=0;i;i=*((long*)i),j++);return j;}
  9. static void In(){int i=depth(),j; for(j=1;j<i;j++) putc(' ',debug);}
  10. #endif
  11. /*-----*//* fast compare s[0:n-1]==t[0:n-1] */
  12. //static char str_cmp(void*s,void*t,int n){
  13. //asm("pushl %esi"); asm("pushl %edi"); asm("cld");
  14. //asm("movl 8(%ebp),%esi"); asm("movl 12(%ebp),%edi");
  15. //asm("movl 16(%ebp),%ecx"); asm("repe"); asm("cmpsb"); asm("pushf");
  16. //asm("popw %ax"); asm("andl $192,%eax");
  17. //asm("xorl $64,%eax"); asm("popl %edi"); asm("popl %esi");}
  18. /*-----*//* fast move s[0:n-1]=t[0:n-1] */
  19. static void str_cpy(void*s,void*t,int n){
  20. asm("pushl %esi"); asm("pushl %edi"); asm("cld");
  21. asm("movl 8(%ebp),%edi"); asm("movl 12(%ebp),%esi");
  22. asm("movl 16(%ebp),%ecx"); asm("rep"); asm("movsb"); asm("popl %edi");
  23. asm("popl %esi");}
  24. /*----- move M forwards 1, but skip certain char sequences; stop at whoa */
  25. int rightC(mark&M,mark whoa){enum{endcomment=1,instring,incomment};
  26. line*R=M.r; char*S=R->s; int c,C=M.c,z=0, n=R==whoa.r?(whoa.c<?R->n):R->n;
  27. char A=C<n?S[C]:LF, B=C<n-1?S[C+1]:LF;
  28. if(A=='/') if(B=='*') {C+=2; z=incomment; /* skip / * */
  29. Z:  if(C<n-1) if(S[C]=='*') if(S[C+1]=='/') {C+=2; z=0; goto E;}
  30.     if(C>=n) {if(R==whoa.r) goto E; /* skip * / */
  31.         C=0; if(!(R=R->next)) goto E; S=R->s; n=R==whoa.r?whoa.c:R->n;}
  32.     else ++C; goto Z;}
  33. /* if(A=='/') if(B=='/') {C=n; goto E;} */ /* C++ // comment, skip to eol */
  34. if(A=='\'') {if(++C>=n) goto E; /* character value */
  35.     if(S[C++]=='\\') {if(C>=n) goto E; /* \ found */
  36.         if(!((S[C]-'0')&~7)) { /* \nnn */
  37.             if(++C<n) if(!((S[C]-'0')&~7)) if(++C<n) if(!((S[C]-'0')&~7)) C++;}
  38.         else if(C<n) C++;}
  39.     if(C<n) if(S[C]=='\'') C++; goto E;}
  40. if(A=='"') {C++; z=instring;
  41. X:  if((A=C<n?S[C]:LF)=='"') {C++; z=0; goto E;} /* string */
  42.     if(C>=n) {if(R==whoa.r) goto E; /* M.r=0 if runs off file */
  43.         C=0; if(!(R=R->next)) goto E; S=R->s; n=R==whoa.r?whoa.c:R->n;}
  44.     else C+=(A=='\\')?2:1; goto X;} /* in string, skip \ and char */
  45. if(A=='*') if(B=='/') {C+=2; z=endcomment; goto E;} /* skip * / */
  46. if(C<n) C++; else {if(R==whoa.r) goto E;
  47.     C=0; if(!(R=R->next)) goto E; /* S=R->s; */ n=R==whoa.r?whoa.c:R->n;}
  48. E: M.c=C<?n; M.r=R; return z;} /* = whether now in string or comment */
  49. /*----- move mark 1 char *//* if runs off file, leaves this->r==0 */
  50. /* void mark::operator++(){if(++c>r->n) {c=0; r=r->next;}} */
  51. /* void mark::operator--(){if(--c<0) if(r=r->prev) c=r->n; else c=0;} */
  52. /*----- check C bracket matching in region R */
  53. val bracketsC(const region&R){enum{endcomment=1,instring,incomment};
  54. mark Q; static char *w,c,b[64],*X="([{}])"; int i,j,k=0,n=0;
  55. for(Q=R.beg;!Q.r?0:Q!=R.end;j=rightC(Q,R.end)) {
  56.     if(!k) if(j==endcomment) {k=2; b[0]='*'; b[1]='/'; n=2;}
  57.     c=*Q; n<?=63;
  58.     if(w=index(X,c)) if(i=w-X,!n?0:i<3?0:b[n-1]+i==5) n--; else b[n++]=i;}
  59. for(i=k;i<n;i++) b[i]=X[b[i]];
  60. if(n>63?0:j==instring) b[n++]='"';
  61. else if(n>62?0:j==incomment) {b[n++]='/'; b[n++]='*';}
  62. b[n]=0; return val(b,n);}
  63. /*-----*/
  64. KF(cbrackets) {val b=bracketsC(B->Mark(N.i)-B->dot);
  65. (B->Mark(N.i)-B->dot).color(White,Magenta);
  66. if(!b.n) {Display="all brackets in region match"; return;}
  67. pr(CW,"unpaired brackets in region: %s",b.s); Display=CW;}
  68. /*-----*//* START OF DIRECTORY READER */
  69. /* format of a directory entry returned by int21 ah=0x4e & ah=4f */
  70. class FCBtime{public: uns short x;
  71.     void strcat(char*B){short hr=(x>>11)&31,min=(x>>5)&63,twosec=x&31;
  72.     pa(B,"%2d.%02d.%02d",hr,min,twosec*2);};};
  73. class FCBdate{public: uns short x;
  74.     void strcat(char*B){static char*Month[]={"@","Jan","Feb","Mar","Apr","May",
  75.     "Jun","Jul","Aug","Sep","Oct","Nov","Dec",".",",",":"};
  76.     short year=(x>>9)&127,month=(x>>5)&15,day=x&31;
  77.     pa(B,"%4d %3s %2d",year+1980,Month[month],day);};};
  78. class dirbuf{public:byte Reservebuf[21],attrib; FCBtime time; FCBdate date;
  79.     uns long size; char name[13];
  80.     void setDTA(){_ax=0x1a00; _dx=(uns int)this; int21();};/* DTA -> user var */
  81.     /*----- read first directory entry into DTA *//* 1 if file found, else 0 */
  82.     byte GetFirst(char*dirname, uns int attrib=0){
  83.     _ax=0x4e00; _cx=attrib; _dx=(uns int)dirname; int21(); return !_carry;};
  84.     /*----- read next directory entry into DTA *//* 1 if file found, else 0 */
  85.     byte GetNext(){_ax=0x4f00; int21(); return !_carry;}
  86.     int get(char*name){setDTA(); return GetFirst(name,~0);};};
  87. dirbuf fileinfo;
  88. unsigned long filesize(char*name){return fileinfo.get(name)?fileinfo.size:-1;}
  89. class direntry{public:
  90.     byte attrib; FCBtime time; FCBdate date; uns long size; char name[13];
  91.     direntry*next;
  92.     direntry(dirbuf&d,direntry*n){reg char*s,*t,*u; attrib=d.attrib;size=d.size;
  93.         for(s=name,t=d.name,u=s+12;s<u;s++,t++) *s=to__lower[byte(*t)];
  94.         name[12]=0; date.x=d.date.x; time.x=d.time.x; next=n;};
  95.     /*----- information about an entry */
  96.     void print(char*B){pr(B,"%-15s%8u ",name,size);
  97.     date.strcat(B); strcat(B," "); time.strcat(B); if(attrib&31) strcat(B," (");
  98.     if(attrib&1)  strcat(B,"readonly,"); if(attrib&2) strcat(B,"hidden,");
  99.     if(attrib&4)  strcat(B,"system,");   if(attrib&8) strcat(B,"volname,");
  100.     if(attrib&16) strcat(B,"dir,");      if(attrib&31) B[strlen(B)-1]=')';};};
  101. class directory{public:direntry**entry; int n; char*name;
  102.     directory(char*Name);
  103.     ~directory(){int i;
  104.         for(i=0;i<n;i++) delete entry[i]; delete entry; delete name;};
  105.     Text copy();};
  106. /*------*/
  107. Text directory::copy(){int i,s;line*L[n+2]; for(i=n+1;i>=0;i--) L[i]=new line();
  108. for(i=0;i<=n;i++) *L[i]-*L[i+1]; for(s=i=0;i<n;i++) s+=entry[i]->size;
  109. pr(CW,"directory %s, has %1d entries, %1d bytes",name,n,s); *L[0]=val(CW);
  110. for(i=0;i<n;i++) {entry[i]->print(CW); *L[i+1]=val(CW);}
  111. return Text(L[0],L[n+1]);}
  112. /*-----*/
  113. KF(copydir){T1.getifn(Fn,"directory to list?"); setrek(T1,T1.copy());
  114. directory D(T1.s); B->dot.yank(D.copy(),0);}
  115. /*-----*/
  116. /* int compare(void*S,void*T,int n){register uns char*s=S,*t=T,*u=s+n;
  117.     while(s<u) if(*s++-*t++) return s[-1]-t[-1]; return 0;} */
  118. /*-----*/
  119. enum {s_dont=0,s_name=1,s_size=2,s_date=3}; static char dirsorttype=s_name;
  120. int ifswop(direntry*a,direntry*b){int i; switch(dirsorttype){
  121. case s_dont: return 0;
  122. case s_name: default: return strcmp(a->name,b->name)>0;
  123. case s_size: return a->size>b->size;
  124. case s_date: i=a->date.x-b->date.x; return i?i>0:(a->time.x>b->time.x);}}
  125. /*----- read a directory */
  126. directory::directory(char*N){name=new char[strlen(N)+1]; strcpy(name,N);
  127. int attrib=~0; char *s,*t; int i,k,l; reg int j; direntry*ch=0; n=0;
  128. dirbuf db; db.setDTA(); if(!db.GetFirst(N,attrib)) {entry=0; return;}
  129. do if(db.name[0]!='.') {n++; ch=new direntry(db,ch);} while(db.GetNext());
  130. reg direntry**e=new direntry*[n];
  131. {reg direntry*dc; for(j=0,dc=ch;dc;j++,dc=dc->next) e[j]=dc;}
  132. for(k=1,i=n-1;k;i--) for(k=j=0;j<i;j++) {reg direntry*a,**b=e+j; /* sort */
  133.     if(ifswop(b[0],b[1])) {a=b[0]; b[0]=b[1]; b[1]=a; k=1;}}
  134. entry=e;}
  135. /*-----*/
  136. int samefile(reg char*s,reg char*t){reg int S=strlen(s),T=strlen(t);
  137. return S>T ?0: strcmp(s,t+T-S) ?0: S==T ?1: t[T-S-1]=='\\';}
  138. /*-----*/
  139. struct {char x; int n; char*s;} dmh[16]={
  140. {0, CR,         " go to marked file or dir"},
  141. {1, -ins,       " new buffer in this dir"},
  142. {1, -ctrl_insert," new subdir in this dir"},
  143. {0, -pageup,    " up a page"},
  144. {0, -pagedown,  " down a page"},
  145. {0, -del,       " delete marked file or dir"},
  146. {1, -home,      " go to parent dir"},
  147. {1, -alt_K,     " copy this dir list into a kill"},
  148. {0, -alt_del,   " drop marked file's buffer"},
  149. {0, -alt_R,     " rename marked file"},
  150. {1, -2,         " exit from this submenu"},
  151. {0, 'N'-64,     " sort by name"},
  152. {0, 'S'-64,     " sort by size"},
  153. {0, 'D'-64,     " sort by date"},
  154. {0, 'X'-64,     " don't sort"},
  155. {1, -alt_end,   " abort this subr call"}}; enum{dmhn=16};
  156. /*-----*/
  157. void filefromdirmenu(val&Dir,char*prompt,char*at/*=0*/){Text*W; directory*D=0;
  158. char*kk,d[256],fn[13],*oldB=B->name; extern char*dirmenuhelp[]; val T; buffer*Z;
  159. mousestate ms,MS; int c,Q,fl,i,I,j,k,n,s,v=gp_Rows-1,w=v-2,X=0,x=0,Y;
  160. B->dotcc=-1; strcpy(d,Dir.s); c=strlen(d); if(c) if(d[c-1]=='\\') d[--c]=0;
  161. if(at?strlen(at)>c:0) strncpy(fn,at+c+1,13); else fn[0]=0; j=0; ms=Jerry;
  162. READDIC: strcpy(d+c,"\\*.*"); delete D; D=new directory(d); x=X; X=0;
  163. YY: if(n=D->n) j%=n; else j=0; for(s=i=0;i<n;i++) s+=D->entry[i]->size;
  164. Jerry.range(D->n?:1,4); Jerry.move(0,0);
  165. if(fn[0]) if(!x?1:j<0) {j=0;
  166.     for(i=0;i<n;i++) if(samefile(D->entry[i]->name,fn)) break; if(i<n) j=i;}
  167. j<?=n-1; d[c]=0; k=w/2; k=n<w?0:((j-w/4)/k)*k; k<?=n-w; k>?=0;
  168. Y: pr(CW,"directory %s, %1d entries, %1d bytes (I was asking \"%s\")",
  169.   D->name,n,s,prompt); display(CW,0,0,Cyan); d[c]='\\';
  170. for(i=k;i<n;i++) {if(i-k>=w) break; CW[1]=' '; D->entry[i]->print(CW+2);
  171.     strcpy(d+c+1,D->entry[i]->name);
  172.     CW[0]=!(Z=buf_named(d))?' ':Z->changed?'*':'o';
  173.     CW[gp_Cols]=0; display(CW,i-k+1,0,Orange);} d[c]=0;
  174. display(n<2? "(ctrl-_ full help, home \032 parent directory, altend aborts)":
  175.   "(\030\031 select, ctrl-_ full help, home \032 parent dir, altend aborts, \
  176. return chooses)",  (i-k+1)>?1,0,Orange);
  177. AA: i=k; k=w/2; k=n<w?0:((j-w/4)/k)*k; k<?=n-w; k>?=0;
  178. if(i!=k) goto Y; Y=j-k+1; if(x!=-mousemove) Jerry.move(j,1);
  179. if(n) scr(Y,1)=sch(2,White); Jerry.mc=1;
  180. switch(x=getkey()){default: goto AA;
  181. case -ins: strcat(d,"\\"); T.n=0; T.s=0; T.getifn(Fn,prompt,_buffer,d);
  182.     findbuf(T)->go_to(); setrek(T1,T.copy()); delete D; Jerry=ms; return;
  183. case -ctrl_insert: strcat(d,"\\"); T.n=0; T.getifn(Fn,"new directory?",0,d);
  184.     display(" ",v,0); _ax=0x3900; _dx=(int)T.s; int21(); if(_carry) beep();
  185.     /** setrek(T1,T.copy()); */ X=1; goto READDIC;
  186. case -alt_end: Jerry=ms; MOAN("aborted");
  187. case -downarrow: if(n) j=(j+1  )%n; break;
  188. case -uparrow:   if(n) j=(j-1+n)%n; break;
  189. case -pagedown:  if(n) j=(j+w)<?(n-1); break;
  190. case -pageup:    if(n) j=(j-w)>?0; break;
  191. case -mousemove: if(n) j=Jerry.y; break;
  192. case -lbutton: case CR: Jerry.mc=0; if(!n) goto AA;
  193.     d[c++]='\\'; strcpy(d+c,D->entry[j]->name);
  194.     if(D->entry[j]->attrib&16) {
  195.         c=strlen(d); strcpy(d+c,"\\*.*"); directory*E=new directory(d);
  196.         delete D; D=E; refreshscreen(); fn[0]=0; j=0; goto YY;}
  197.     delete D; strcpy(Dir.s,d); Dir.n=strlen(d); Jerry=ms; return;
  198. case -del: if(!n) goto AA; i=D->entry[j]->attrib&0x18;
  199.     if(i&0x16) goto DD; if(i) goto AA; /* volume name */
  200.     beep(); if(!yesno("do you really want to delete this PC file?")) goto AA;
  201.     beep(); i=yesno("do you REALLY want to delete this PC file?");
  202.     display(" ",v,0);if(!i) goto AA; d[c++]='\\'; strcpy(d+c,D->entry[j]->name);
  203.     _ax=0x4100; _dx=(int)d; int21(); d[--c]=0; X=1; goto READDIC;
  204. DD: beep(); i=yesno("do you really want to delete this directory?");
  205.     display(" ",v,0);if(!i) goto AA; d[c]='\\'; strcpy(d+c+1,D->entry[j]->name);
  206.     _ax=0x3a00;_dx=(int)d; int21(); if(_carry)beep(); d[c]=0; X=1; goto READDIC;
  207. case -home: delete D;
  208.     c=k=strlen(d); for(i=0;i<k;i++) if(d[i]=='\\') c=i; d[c]=0; refreshscreen();
  209.     if(c<k-1) strncpy(fn,d+c+1,13); else fn[0]=0; j=0; goto READDIC;
  210. case -alt_K: setref(f,©dir); strcpy(d,!D?"D_isnull":D->name?:"(null)");
  211.     setrek(T1,copyof(d));
  212.     if(D) {nkill++; killring[nkill&=15].clear(); killring[nkill]=D->copy();}
  213.     j=0; goto READDIC;
  214. case -alt_del: d[c]='\\'; strcpy(d+c+1,D->entry[j]->name); X=1;
  215.     Z=buf_named(d); d[c]=0; if(Z) if(deletebuffer(Z))goto READDIC; X=0; goto AA;
  216. case -alt_R: d[c]='\\'; d[c+1]=T.n=0; T.s=0;
  217.     T.getifn(Fn,"rename this file as:",0,d); strcpy(d+c+1,D->entry[j]->name);
  218.     _ax=0x5600; _dx=(int)d; _di=(int)T.s; int21(); if(_carry) beep(); d[c]=0;
  219.     display(" ",v,0); X=1; goto READDIC;
  220. case '_'-64: for(i=0;kk=dirmenuhelp[i];i++) display(kk,i+1,0,Cyan); X=1;
  221.     display("(type any char to continue)",i+1,0,Cyan); getkey(); goto READDIC;
  222. case 'N'-64: dirsorttype=s_name; goto RESORT;
  223. case 'S'-64: dirsorttype=s_size; goto RESORT;
  224. case 'D'-64: dirsorttype=s_date; goto RESORT;
  225. case 'X'-64: dirsorttype=s_dont;
  226. RESORT: X=0; strcpy(fn,D->entry[j]->name); goto READDIC;
  227. case -f1: case -rbutton: case -mbutton: Q=0;
  228.     for(i=0;i<dmhn;i++) display((n?:dmh[i].x)?dmh[i].s:" ",i+1,47,Cyan);
  229.     MS=Jerry; Jerry.move(0,0); Jerry.range(dmhn,4); i=0; scr(1,47)=1+256*White;
  230.     MENU: I=i; scr(i+1,47)=1+256*White; if(Q!=-mousemove) Jerry.move(i,0);
  231.     switch(Q=getkey()) {
  232.     case -uparrow: i=(i+dmhn-1)%dmhn; break;
  233.     case -downarrow: i=(i+1)%dmhn; break;
  234.     case -mousemove: i=Jerry.y; break;
  235.     case -mbutton: case -rbutton: i=10;
  236.     case CR: case -lbutton: if(n?:dmh[i].x) inject(dmh[i].n); else break;
  237.         Jerry=MS; for(i=0;i<dmhn;i++) display(" ",i+1,47,White); goto Y;}
  238.     if(I!=i) scr(I+1,47)=' '+256*White; goto MENU;}
  239. if(n) scr(Y,1)=sch(' ',White); goto AA;}
  240. /*-----*/
  241. KF(gotodir){char*s; strcpy(s=Fn.s,B->name); int i,j=-1,n=Fn.n=strlen(Fn.s);
  242. for(i=0;i<n;i++) if(s[i]=='\\') j=i;
  243. if(j<1) MOAN("this buffer has no file name and thus no directory"); s[j]=0;
  244. Fn.n=j; filefromdirmenu(Fn,"file?",B->name); findbuf(Fn)->go_to();}
  245. /*-----*/
  246. void bitpack(char*s,char*t,int n){int i; reg char*u;
  247. for(i=0;i<n-7;i+=8) {u=t+i; *s++=
  248.     (u[0]<<7)+(u[1]<<6)+(u[2]<<5)+(u[3]<<4)+(u[4]<<3)+(u[5]<<2)+(u[6]<<1)+u[7];}
  249. if(i==n) return; *s=0; for(;i<n;i++) *s=(*s<<1)|t[i];}
  250. /*-----*/
  251. void bitexp(reg char*t,reg char*s,int n) {reg char *a=t+n,c;
  252. if(s) while(t<a) {c=*s++; *t++=c&1; *t++=(c&2)>>1; *t++=(c&4)>>2; *t++=(c&8)>>3;
  253.     *t++=(c&16)>>4; *t++=(c&32)>>5; *t++=(c&64)>>6; *t++=(c&128)>>7;}
  254. else while(t<a) *t++=0;}
  255. /*-----*/
  256. void bitcopy(reg char*t,reg char*s,int n) {reg char *a=t+((n+7)>>3);
  257. if(t) if(s) while(t<a) *t++=*s++; else while(t<a) *t++=0;}
  258. /*-----*//* MY OWN FORMATTED PRINT */
  259. /**** These print functions rely on C function arg values being stored at */
  260. /* (sizeof(each arg's value) rounded up to a multiple of 4) byte intervals ****/
  261. char*pr(char*B,char*F0,...) { return pr_(B,             F0,(int*)(&F0)+1);}
  262. char*pa(char*B,char*F0,...) { return pr_(B+strlen(B),   F0,(int*)(&F0)+1);}
  263. void pf(int f,char*F0,...) {if(f>=0) pr_((char*)f,      F0,(int*)(&F0)+1);}
  264. void pb(char*F0,...) {               pr_((char*)_buffer,F0,(int*)(&F0)+1);}
  265. void ps(char*F0,...) {               pr_(0,             F0,(int*)(&F0)+1);}
  266. void p_r(char*B,char*F0,...) {       pr_(B,             F0,(int*)(&F0)+1);}
  267. /* pr=string B, pa=string B append, pf=file f, pb=current buffer, ps=screen */
  268. /*-----*/
  269. char*pr_(char*S,char*F0,int*args){static char d[]="0123456789abcdef"; val*v;
  270. char*XX="  format char\n"; char Buf[1024],*F=F0,*t,*u,*bb,*b,W[1024],s;
  271. int c,m,n,p,q,r,rs,i,j,w,*P=args; uns long X,V; bb=b=(int)S>=4096?S:W;
  272. /* Gnu C subr args are at 4*n-byte intervals */
  273. while(c=*F++) {if(c!='%') {*b++=c; continue;} rs=s=w=0; p=1;
  274. Z: switch (c=*F++) { /* w=wanted width, m=actual width, p=min width zerofill */
  275.     case 0: goto END;
  276.     case 'k': s=1;
  277.     case 'K': ((val*)(*P++))->expandkeyseq(b,s); b+=strlen(b); break;
  278.     case ' ': if(!s) s=' '; goto Z;
  279.     case '-': rs=1; goto Z;
  280.     case '+': s='+'; goto Z;
  281.     case '.': p=0; while(isdigit(c=*F++)) p=10*p+c-'0'; F--; goto Z;
  282.     case '0': p=-1; goto Z;
  283.     case'1':case'2':case'3':case'4':case'5':case'6':case'7':case'8':case'9':
  284.         w=c-'0'; while(isdigit(c=*F++)) w=10*w+c-'0'; F--; goto Z;
  285.     case 'c': t=(char*)(P++); n=1; goto STR; /****works as PC is little-endian*/
  286.     case 's': if(!(t=(char*)*P++)) t="(null)"; n=strlen(t); goto STR;
  287.     case 'S': if(!(t=(char*)*P++)) t="(null)"; n=strlen(t); goto TT;
  288.     case 't': n=strlen(t=keysort[-(int)*P++]); goto STR;
  289.     case 'T': u=b; b=(b+1)>?(bb+((int)*P++)); while(u<b) *u++=' '; break;
  290.     case 'v': v=(val*)*P++; t=v->s; n=t?v->n:0; if(v->magic()) goto MAGIC;
  291. TT:     *b++='"';
  292.         for(i=0;i<n;i++) {w=strlen(u=chname(t[i])); for(j=0;j<w;j++) *b++=u[j];}
  293.         *b++='"'; break;
  294. MAGIC:  n&=0x00ffffff; *b++='"';
  295.         for(i=0;i<n;i++) {if(bit(t+n+1,i)) *b++='`';
  296.             w=strlen(u=chname(t[i])); for(j=0;j<w;j++) *b++=u[j];}
  297.         *b++='"'; break;
  298.     case 'd': X=*P++; if((long)X<0) {X=-X; s='-';} r=10; goto NUM;
  299.     case 'o': X=*P++; r=8;  goto NUM;
  300.     case 'u': X=*P++; r=10; goto NUM;
  301.     case 'x': X=*P++; r=16; goto NUM;
  302.     default: *b++=c;}; goto Y;
  303. NUM: if(p<0) p=s?w-1:w; V=X;
  304.     m=0; if(V?:p) do m++; while(V/=r); m>?=p; /* m=#digits */ n=m+(s?1:0);
  305.     if(!rs) for(q=n;q<w;q++) *b++=' '; if(s) *b++=s;
  306.     for(c=m-1;c>=0;c--) {b[c]=d[X%r]; X/=r;} b+=m; goto J;
  307. STR: if(!rs) for(q=n;q<w;q++) *b++=' '; u=t+n; while(t<u) *b++=*t++;
  308. J:  if(rs) for(q=n;q<w;q++) *b++=' '; Y:;}
  309. END: *b=0;
  310. if((int)S==_buffer) *B+=W; /* insert into current buffer */
  311. else if(!S) wr(W); /* write to screen */
  312. else if((int)S<4096) write((int)S,W,b-W); /* write on a file */ return b;}
  313. /*-----*/
  314. char**readtext(char*file){char*s; int F; if((F=open(file,0x8001))<0) return 0;
  315. dirbuf d; if(!d.get(file)) return 0; int i,j,n=d.size; char*text=new char[n+1];
  316. n=read(F,text,n); close(F); if(!n?:text[n-1]!=LF) text[n++]=LF;
  317. for(i=j=0;i<n;i++) if(text[i]==LF) j++; char**lin=new char*[j+1]; lin[0]=text;
  318. for(i=0,j=1;i<n;i++) if(text[i]==LF) {text[i]=0; lin[j++]=&text[i+1];}
  319. for(s=lin[0]-2,i=1;i<j;i++) if(s[2]) if(*(s=lin[i]-2)==CR) *s=0; lin[j-1]=0;
  320. return lin;}
  321. /*-----*//* START OF SPELLING CHECKER */
  322. typedef unsigned char byte; /* V=vowel, C=consonant, E=anything but 'e' */
  323. static char*Buf,*P,*Be,conj[5]=" ai ",cw[1024],gems[]=" 23";
  324. static int nconj=4,Wn=0,sn,D,pradd,alteredfrom,trace,pfstart,RV;
  325. #define upb(array) (sizeof(array)/sizeof((array)[0]))
  326. #define forways(m,w) for(m##e=(m=w.way)+w.wayn;m<m##e;m++)
  327. enum{MAXDICT=200000,NWORDS=20000};
  328. enum{noGem=0,Gem=1,GemUK=2};
  329. enum{e_normal=0,e_keep=1,e_soften=2};
  330. enum{supine=0,conjA=1,conjI=2,notlatin=3};
  331. class Decl{public: uns int gem:2,cap:1,conj:2,e:2,bad:1;};
  332. class Word{public: char*s; byte n; Decl decl;
  333.     inline char*suf(){return s+n+1;};
  334.     inline void operator=(val&v){s=v.s; n=v.n;};};
  335. class Way {public: char*del,*add; byte deln,addn;};
  336. class Suf {public: Way*way; byte wayn;};
  337. static val*pre; static int npre; static Word word[NWORDS]; /******/
  338. static Suf suf[256],desuf[32];
  339. static int findword(val w);
  340. static int addsuffix(char*word,int Wl,Decl decl,Way&m,char*saveend);
  341. static byte _CV[32]=
  342.   {0,0,1,1,1,0,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,0,0,0,0,0};
  343. inline int  cons(char c){return   _CV[c&31];}
  344. inline int vowel(char c){return 1-_CV[c&31];}
  345. #define forwords(w) for(w=sn;w<Wn;w++)
  346. #define forall(w) for(w=1;w<Wn;w++)
  347. #define forsuffs(s,w) for(s=w.suf();*s;s++)
  348. /*-----*/
  349. class wordlist{public: char**s; int n;
  350. wordlist(int i=1){s=new char*[16]; s[0]=""; n=1;}; /* word 0 is dummy */
  351. int place(char*w){int i,j,k,m; if(n<2) return -1; int wl=strlen(w);
  352.     for(j=0x8000,m=0;j>=1;j>>=1) if((k=m+j)<n) {
  353.         i=strncmp(w,s[k]+1,wl+1); if(!i) return k; if(i>0) m=k;}
  354.     return -m-1;};
  355. inline int contains(char*w){return place(w)>0;};
  356. int operator+(char*w){char**t; int i,j,k,m; int wl=strlen(w);
  357.     if((k=place(w))>0) return k; k=-k; t=s;
  358.     if(!(n&15)) {s=(char**)myalloc((n+16)*sizeof(char*));
  359.         for(i=0;i<k;i++) s[i]=t[i];}
  360.     for(i=n;i>k;i--) s[i]=t[i-1];
  361.     s[k]=(char*)myalloc(wl+2); str_cpy(s[k]+1,w,wl+1);
  362.     s[k][0]=0; if(s!=t) delete t; n++; return k;};
  363. void empty(){int i; for(i=1;i<n;i++) delete s[i]; delete s; s=new char*[16];
  364.     s[0]=""; n=1;};
  365. Text copy(int min){int i,j,m=0; for(i=1;i<n;i++) if(s[i][0]>=min) m++;
  366.     line*L[m+1]; for(i=0;i<=m;i++) L[i]=new line();
  367.     if(min>=0) {for(i=1,j=0;i<n;i++) if(s[i][0]>=min) {
  368.         *L[j]-*L[j+1]; *L[j++]=val(s[i]+1);}}
  369.     else for(i=1;i<n;i++) {*L[i-1]-*L[i]; strcpy(CW,s[i]+1); j=strlen(CW);
  370.         CW[j++]='/';CW[j++]='#'; CW[j++]='0'+s[i][0]; CW[j]=0; *L[i-1]=val(CW);}
  371.     return Text(L[0],L[m]);};};
  372. wordlist appendix(1); int&appendixsize=appendix.n;
  373. /*----- ONE-LINERS */
  374. static void skipsp(){while(*P==' ') P++;}
  375. static void findeol(){while(*P!='\n') P++; P++;}
  376. static int nextis(char c){reg int j; skipsp(); if(j=*P==c) P++; return j;}
  377. static int getn(){reg int n=0; while(isdigit(*P))n=n*10+*P++-'0'; return n;}
  378. static void X(char*moan) {Wn=0;
  379.     pr(CW,"bad data at %dth byte of dictionary: %s",P-Buf,moan); MOAN(CW);}
  380. /*-----*/
  381. static char*getwword(byte&N){reg char c,*s=P,*t; reg int n=0; skipsp();
  382. while(isalnum(c=*P)?1:c=='!'?:c=='+') {P++; n++;}
  383. if(!(N=n)) return 0; t=new char[n+1]; str_cpy(t,s,n); t[n]=0; return t;}
  384. /*-----*/
  385. static val getheadword(){reg char *s; reg int n=0; skipsp(); s=P;
  386. if(*P=='-'?:*P=='<') {n++; P++;} while(isalnum(*P)?:*P=='\'') {n++; P++;}
  387. if(!n) return val(0,0); return val(s,n);}
  388. /*-----*/
  389. static void readways(Suf&S){Way way[32],*m; bzero(m=way,32*sizeof(Way));
  390. do {if(m-way>=32) X("this suffix has > 32 methods");
  391.     m->del=getwword(m->deln)?:"";
  392.     if(nextis(':')) m->add=getwword(m->addn)?:"";
  393.     else {m->add=m->del; m->del=""; m->addn=m->deln; m->deln=0;}
  394.     m++;} while(nextis(','));
  395. S.way=new Way[S.wayn=m-way]; memcpy(S.way,way,S.wayn*sizeof(Way));}
  396. /*-----*/
  397. static void setup(){static Way nullway={"","",0,0}; reg Way*m,*me; reg Word*W;
  398. int i,j,k,l,n,s,w,wn; char *p,*q,T[256],TT[256],*Q[256+1]; byte sf[256];
  399. bzero(word,sizeof(word)); Buf=new char[MAXDICT]; val DF(0,0),v;
  400. suf[0].wayn=1; suf[0].way=&nullway; suf[1]=suf[0]; i=1;
  401. strcpy(T2t.s,dictname); T2t.n=strlen(T2t.s);
  402. DF.getifn(T2t,"dictionary file name?");
  403. D=open(T2t.s,0x4001); /*read text*/ Display="reading dictionary"; showmoan();
  404. Be=Buf+(n=read(D,Buf,MAXDICT)); if(n<=0) MOAN("I can't read the dictionary");
  405. if(n==MAXDICT) MOAN("dictionary too big for buffer");
  406. if(Be[-1]!='\n') *Be++='\n'; pradd=0;
  407. for(P=Buf;strncmp(P,"--------",8);findeol()) if(P>=Be) X("bad dictionary");
  408. findeol(); for(i=0;i<32;i++) {desuf[i].way=0; desuf[i].wayn=0;}
  409. while(nextis('^')) {if(!isalpha(*P)) X("bad char after '^'"); i=(*P)&31; P+=2;
  410.     readways(desuf[i]); findeol();}
  411. for(i=0,sn=1;;sn++) {findeol(); v=getheadword(); if(!v.n) X("bad headword");
  412.     Q[sn]=P; word[sn]=v; if(v.s[0]=='<') i++; else if(v.s[0]!='-') break;
  413.     if(sn>=256-1) X("too many suffixes");}
  414. Wn=sn; pre=new val[npre=i]; RV=findword(val("-RV",3));
  415. for(j=0,i=1;i<sn;i++) {reg Suf&S=suf[i]; P=Q[i]; S.wayn=0; S.way=0;
  416.     if(nextis('=')) readways(S); Q[i]=P;
  417.     if(word[i].s[0]=='<') pre[j++]=val(word[i].s+1,word[i].n-1);}
  418. for(j=1;findeol(),P<Be;j++) {reg Word&W=word[j]; char*s; Word*V;
  419.     if(j>=NWORDS) X("too many entries");
  420.     if(j<=sn) P=Q[j]; else {v=getheadword(); if(!v.n) X("bad headword"); W=v;}
  421.     k=W.n;
  422.     if(j>1) if(strncmp(word[j-1].s,W.s,W.n+1)>=0) X("entry out of ascii order");
  423.     if(k>1)if(W.s[k-1]=='e')if(W.s[k-2]=='c'?1:W.s[k-2]=='g') W.decl.e=e_soften;
  424.     for(i=0;i<sn;i++) sf[i]=0;
  425.     while(nextis('/')) {skipsp(); /* stop at / \\ \n space */
  426.         for(s=P;*P=='/'?0:*P=='\\'?0:*P=='\n'?0:*P!=' ';P++);
  427.         i=(n=P-s)==1 ? *s : n==2 ? *s+256*s[1] : 0;
  428.              if(i=='2')         W.decl.gem=Gem; /* doubles last letter */
  429.         else if(i=='3')         W.decl.gem=GemUK; /* ditto, optional */
  430.         else if(i=='X')         W.decl.bad=1; /* illegal word */
  431.         else if(*s=='#')        ; /* start of marker used by putappendix */
  432.         else if(i=='!'+256*'a') W.decl.conj=conjA;
  433.         else if(i=='!'+256*'i') W.decl.conj=conjI;
  434.         else if(i=='!'+256*'n') W.decl.conj=notlatin;
  435.         else if(i=='K'+256*'E') W.decl.e=e_keep;  /* -e stays before vowel */
  436.         else if(i=='P'+256*'N') W.decl.cap=1;
  437.         else if(i=='N'+256*'S') sf[0]=255; /* not self */
  438.         else if((*s=='-'?:*s=='<')?n>1:0) {k=1; if(s[n-1]=='*') {k=255; n--;}
  439.             if(!(l=findword(val(s,n)))) X("not a known suffix"); sf[l]=k;}
  440.         else X("rubbish after '/'");}
  441.     skipsp(); if(*P!='\n') X("rubbish after entry");
  442.     for(i=l=0;i<sn;i++) l+=(sf[i]<?2);
  443.     p=new char[W.n+1+l+1]; str_cpy(p,W.s,W.n); p[W.n]=0; q=p+W.n+1;
  444.     if(k=sf[0]) *q++=k; for(i=1;i<sn;i++) if(k=sf[i]) {*q++=i; if(k-1) *q++=k;}
  445.     *q=0; W.s=p;}
  446. close(D); delete Buf; Wn=j;
  447. for(pfstart=1;pfstart<sn;pfstart++) if(word[pfstart].s[0]=='<') break;
  448. pr(CW,"dictionary read: %d words",Wn); Display=CW; showmoan();}
  449. /*-----*/
  450. static int addsuffix(val w,Decl decl,Way&m,char*saveend){
  451. /* try a form of a suffix. Return 0 if this suffix won't work this way */
  452. int i; reg char dn=m.deln,*s,*t; if(w.n-(w.s[0]=='-'?0:1)<dn) return 0;
  453. #ifdef DB
  454. debug=fopen("t$debug","a"); In();
  455.     fprintf(debug,"addsuffix(%s - %s + %s)\n",w.s,m.del,m.add); fclose(debug);
  456. #endif
  457. if(!dn) {saveend[0]=0; s=w.s+w.n;}
  458. else {if(m.del[0]=='e') switch(decl.e) {
  459.         default: break;
  460.         case e_keep: return 0;
  461.         case e_soften: if(!index("iey",m.add[0])) return 0;}
  462.     for(s=w.s+w.n-dn,t=m.del;*t;t++,s++){
  463.         if(*t=='V') if(vowel(*s)) continue;
  464.         if(*t=='C') if(cons(*s))  continue;
  465.         if(*t!=*s) return 0;}
  466.     if(dn) str_cpy(saveend,s=w.s+w.n-dn,dn); saveend[dn]=0;}
  467. if(t=m.add) if(*t) {
  468.     if(!dn) if(vowel(*t)?:*t=='y') if(decl.gem) {*s=s[-1]; s++;}
  469.     while(*t) if(*t=='!') switch(t++,decl.conj){
  470.     case conjA: *s++='a'; *s++='t'; break;
  471.     case conjI: *s++='i'; *s++='t'; default:;}
  472.     else if(*t=='+') {s++; t++;} else *s++=*t++;} *s=0;
  473. alteredfrom=w.n-dn; return s-w.s;} /* return length of suffixed word */
  474. /*-----*/
  475. static int findword(val w){int i,j,k,l,n; if(!w.s) return 0;
  476. for(j=0x8000,n=0;j>=1;j>>=1) if((k=n+j)<Wn) {/* word 0 in dictionary is dummy */
  477.     i=strncmp(w.s,word[k].s,w.n); if(!i) if(w.n<word[k].n) i=-1;
  478.     if(!i) return k; if(i>0) n=k;} return 0;}
  479. /*-----*/
  480. int checkderivs(val w,val u,Word&U,Decl decl){char*sf=U.suf(),rv; Word*Q;
  481. #ifdef DB
  482. debug=fopen("t$debug","a"); In();
  483. fprintf(debug,"checkderivs(%s,%s as %s)",w.s,u.s,U.s);
  484. int I; for(I=0;I<16;I++) fprintf(debug," %02x",byte(sf[I])); putc('\n',debug);
  485. fclose(debug);
  486. #endif
  487. if(Breakin()) return 0; if(u.n>w.n+1) return 0; /* word has got too long */
  488. int i,j,k,newun; Way*m,*me; byte s; if(byte(*sf)==255) sf++;
  489. for(;s=*sf;sf++) {Word&W=word[s]; Suf&S=suf[s]; if(rv=(byte(sf[1])==255)) sf++;
  490. #ifdef DB
  491.     debug=fopen("t$debug","a"); In();
  492.     fprintf(debug,"checkderivs(%s,%s as %s: %s)\n",w.s,u.s,U.s,W.s);
  493.     In(); fprintf(debug,"S.wayn=%1d\n",S.wayn);
  494.     fclose(debug);
  495. #endif
  496.     if(W.s[0]=='<') {Way&P=S.way[0]; i=P.addn+u.n; char T[i+1],*s; val v(T,i);
  497.         str_cpy(T,P.add,P.addn); str_cpy(T+P.addn,u.s,u.n+1);
  498.         if(!strncmp(w.s,T,v.n+1)) return 1; s=W.suf();
  499.         if(byte(s[0])==255?!s[1]:0) {Word V; V.decl=U.decl; V.n=U.n; char*t;
  500.             char Y[512]; str_cpy(V.s=Y,U.s,i=U.n+1); t=Y+i; s=U.s+i;
  501.             while(*s) /* if(byte(*s)>=pfstart) break; else */ *t++=*s++; *t=0;
  502.             Q=&V;}
  503.         else Q=&W;
  504.         if(checkderivs(w,v,*Q,decl)) return 1;}
  505.     else  if(!S.wayn) {if(checkderivs(w,u,W,decl)) return 1;}
  506.     else for(me=(m=S.way)+S.wayn;m<me;m++) {char T[16];
  507.         if(newun=addsuffix(u,decl,*m,T)) {
  508.             if(!strncmp(w.s,u.s,newun+1)) k=byte(*W.suf())!=255;
  509.             else k=checkderivs(w,val(u.s,newun),W,W.decl) ?: !rv?0:
  510.                 checkderivs(w,val(u.s,newun),word[RV],W.decl);
  511.             strcpy(u.s+u.n-m->deln,T); if(k) return k; break;}}}
  512. return 0; /* fault: no way to make the suffix wanted */}
  513. /*-----*/
  514. val Stak[64];
  515. /*-----*/
  516. static int checkwd(val w,val u,int D=0){if(D>=63?:Breakin()) return 0;
  517. #ifdef DB
  518. debug=fopen("t$debug","a"); In();
  519.     fprintf(debug,"checkwd(%s,%s)\n",w.s,u.s); fclose(debug);
  520. #endif
  521. int i,j,k,l,n; char T[512]; Way*m,*me; byte s; Suf*S; val*pr;
  522. for(i=0;i<D;i++) if(!strncmp(u.s,Stak[i].s,u.n+1)) return 0; Stak[D]=u;
  523. //for(j=i=0;i<D;i++) if(!strncmp(u.s,Stak[i].s,u.n+1)) j=i;
  524. // if(j) {debug=fopen("t$debug","a"); In();
  525. //  fprintf(debug,"same as Stak[%d]\n",j); fclose(debug); return 0;}
  526. //Stak[D]=u;
  527. /* to stop a loop that developed e.g. ble -> blee -> ble -> etc */
  528. for(j=0x8000,n=0;j>=1;j>>=1) if((k=n+j)<Wn) {Word&W=word[k];
  529.     i=strncmp(u.s,W.s,u.n+1); if(!i) if(u.n<W.n) i=-1;
  530.     if(!i) {if(W.decl.bad) return 0; /* word u found exactly */
  531.         if(!strncmp(w.s,u.s,u.n+1)) return (s=*W.suf())==255?0:k;
  532.         str_cpy(T,W.s,u.n+1); /* find if u can be extended to w */
  533.         if(checkderivs(w,val(T,u.n),W,W.decl)) return 1;
  534.         if(W.decl.gem==GemUK){Decl d=W.decl; d.gem=noGem; /* UK/USA variation */
  535.             if(checkderivs(w,val(T,u.n),W,d)) return 1;}
  536.         break;}
  537.     if(i>0) n=k;}
  538. for(pr=pre+npre-1;pr>=pre;pr--) if(*u.s==*pr->s) if(!strncmp(u.s,pr->s,j=pr->n))
  539.   if(k=checkwd(w,val(u.s+j,u.n-j),D+1)) return k; /*try removing prefix from u*/
  540. for(S=desuf+(u.s[u.n-1]&31),me=(m=S->way)+S->wayn;m<me;m++) {
  541.     if(u.n>m->deln) if(!strncmp(u.s+u.n-m->deln,m->del,m->deln)) {
  542.         if(u.n-m->deln+m->addn>511) break;
  543.         str_cpy(T,u.s,u.n+1); /* try removing suffix from u */
  544.         str_cpy(T+u.n-m->deln,m->add,m->addn+1); j=strlen(T);
  545.         if(m->addn>m->deln) if(j>2) if(T[j-1]==T[j-2]) if(T[j-2]==T[j-3]) break;
  546. /* to stop a loop that developed: ble -> blee -> bleee -> etc */
  547.         if(k=checkwd(w,val(T,j),D+1)) return k;}}
  548. if(u.n>2) if(cons(i=u.s[u.n-1])) if(i==u.s[u.n-2]) { /* doubled consonant */
  549.         str_cpy(T,u.s,u.n-1); T[j=u.n-1]=0;
  550.     k=checkwd(w,val(T,j),D+1); u.s[u.n++]=i; return k;}
  551. return 0;}
  552. /*-----*/
  553. KF(checkspelling){char*eor="end of region"; int z=N.i?0:N.n; if(!N.n) N.i=0;
  554. int i,j,k; mark X,Y=B->dot,Z=B->eof(); char *S,T[512]; B->dotcc=B->dotcc2=-1;
  555. if(N.i) {Z=B->Mark(N.i); if(Z<Y) {X=Z; Z=Y; Y=X; setmark(N); B->dot=Y;}}
  556. LOOP: X=Y; if(Breakin()) {B->dot=Y; Moan="user breakin"; return;}
  557. if(X.is_alnum()) X.skip_alnum(1); else X.find_alnum(0,0);
  558. if(X.eol()) {if(X.r==Z.r) {Display=eor; return;} ++X;
  559.     if(!X.r) {Moan="hit eof"; return;} Y=X; goto LOOP;}
  560. Y=X; SW: Y.skipword(1); if(Y.r==Z.r) if(Y.c>Z.c) {Display=eor; return;}
  561. B->dot=Y; B->display(); (X-Y).color(White,Magenta);
  562. if(X.r!=Y.r) MOAN("bad craziness: end-of-line in word!");
  563. if(X.c>=Y.c) MOAN("no word found"); if(Y.c-X.c>500) MOAN("word too long");
  564. S=X.r->s+X.c; T[j=(Y.c-X.c)<?511]=0;
  565. for(k=0,i=j;k<j;k++) if(isdigit(T[k]=to_lower(S[k]))) i--;
  566. if(!Wn) setup(); i=!i ?: checkwd(val(T,j),val(T,j)) ?: appendix.contains(T);
  567. if(!i) if(Y.c<Y.r->n-1) if(*Y=='\'') if(++Y,Y.is_alnum()) goto SW; else --Y;
  568. if(i) if(!N.i) {Display="spelt correctly"; return;} else goto LOOP;
  569. if(T1.n) {i=appendix+T; appendix.s[i][0]=1; goto LOOP;}
  570. B->display(); B->dotcc=B->dotcc2=-1;
  571. k=!N.i?1:yesno("misspelt or unknown word: shall I stop and let you change it?");
  572. if(!k?:z) {
  573.     j=yesno("misspelt or unknown word: shall I list it as vocabulary?");
  574.     i=appendix+T; appendix.s[i][0]=j?1:0;}  if(!k) goto LOOP;
  575. (X-Y).color(White,Magenta); Moan="misspelt or unknown word";}
  576. /*-----*/
  577. KF(getappendix){checkspelling(N.n?N:val(1),val("y",1),val(0,0));}
  578. /*-----*/
  579. KF(putappendix){Text A=appendix.copy(N.i<?1); B->changed=1;
  580. *(B->dot.r->prev?:&B->text)-*A.beg; *A.end-*B->dot.r; B->text.next->prev=0;
  581. if(yesno("shall I also empty the dictionary's appendix?")) appendix.empty();}
  582. /*-----*/
  583. KF(emptyappendix){appendix.empty();}
  584. /*-----*/
  585. int bottommenu(
  586.   char*prompt,int nrb,char*codes,char**names,int L/*=8*/,int defolt/*=0*/){
  587. mousestate ms; ms=Jerry; int c,j,k,m,n=strlen(codes),N=n*L,p; char*s,MN[L*n+1];
  588. Jerry.range(4,n*L); Jerry.move(0,(k=defolt)*L); Jerry.mc=1; c=Jerry.bd=0;
  589. for(c=0;c<L*n;c++) MN[c]=' '; p=strlen(prompt);
  590. char ls[gp_Cols*2],*q=(char*)&scr(gp_Rows-1,0); str_cpy(ls,q,gp_Cols*2);
  591. for(c=0;c<n;c++) for(j=strlen(s=names[c])-1;j>=0;j--) MN[L*c+j]=s[j]; MN[N]=0;
  592. display(prompt,gp_Rows-1,0,Magenta); displayn(MN,gp_Rows-1,p,White+8,Blue);
  593. M1: if(c!=-mousemove) Jerry.move(0,k); counterchange(scr(gp_Rows-1,k+p));
  594. c=getkey(); if(c>='a') if(c<='z') c-=32; counterchange(scr(gp_Rows-1,k+p));
  595. if(Jerry.buttons) {c=-mousemove; goto M1;} switch(c){
  596. case -lbutton: case CR: m=k/L; goto RET;
  597. case -mousemove: k=Jerry.x; goto M1;
  598. case -leftarrow: k=(k-1)%N; goto M1;
  599. case -rightarrow: k=(k+1)%N; goto M1;
  600. case -mbutton: case -rbutton: m=nrb; goto RET;
  601. default: for(m=0;m<n;m++) if(c==codes[m]) goto RET;} goto M1;
  602. RET: Jerry=ms; str_cpy(q,ls,gp_Cols*2); return m;}
  603. /*-----*/
  604. KF(menu){int i,j,k,n,u,v; char*s,**z; uns short*p,*q,*r; subr*sb; mark MK;
  605. uns short scrsave[gp_Cols*gp_Rows]; int ss=gp_Cols*gp_Rows*2;
  606. typedef struct{char submenu; subr*sub; char*text;} menuline;
  607. static menuline _bindmenu[]={{6,0,0},
  608. {0,&bindkeybuf,"bind key to current buffer"},
  609. {0,&bindkeymacro,"bind key to current macro"},
  610. {0,&bindkeysubr,"bind key to subroutine"},
  611. {0,&prbindings,"insert list of key bindings"},
  612. {0,&prkeys,"insert list of keys & key bindings"},
  613. {0,&unbindkey,"unbind key"}};
  614. static menuline*bindmenu=_bindmenu;
  615. static menuline _buf[]={{14,0,0},
  616. {0,&buffermenu,"go to buffer by menu of buffers"},
  617. {0,&gotodir,"go to file by menu of DOS directory"},
  618. {0,&gotonbuf,"go to next buffer in chain"},
  619. {0,©dir,"insert copy of DOS directory"},
  620. {0,&delbuf,"remove current buffer (not its file)"},
  621. {0,&insertfile,"insert text of a file"},
  622. {0,&mergefile,"merge a file into region in ascii order."},
  623. {0,&openfile,"go to file"},
  624. {0,&prbuffers,"list buffers with bindings, in form obeyable by readmacros"},
  625. {0,&renamebuffer,"change filename of buffer"},
  626. {0,&restorebuf,"re-read buffer from its file"},
  627. {0,&showinfo,"show information about buffer"},
  628. {0,&writebuffer,"copy buffer to a file"},
  629. {0,&savebuffer,"copy buffer to its file"}};
  630. static menuline*buf=_buf;
  631. static menuline _c[]={{3,0,0},
  632. {0,&cbrackets,"check region for C bracket matching"},
  633. {0,&cchrstr,"convert \\sequence to literal char"},
  634. {0,&cstrchr,"convert char to \\sequence if needed in C string"}};
  635. static menuline*c=_c;
  636. static menuline _Case[]={{5,0,0},
  637. {0,&capitalize,"capitalize word"},
  638. {0,&lwcasewords,"lowercase word"},
  639. {0,&upcasewords,"uppercase word"},
  640. {0,&lwcaseregion,"lowercase region"},
  641. {0,&upcaseregion,"uppercase region"}};
  642. static menuline*Case=_Case;
  643. static menuline _crlf[]={{3,0,0},
  644. {0,&finalcr,"add CR marker in all lines in region"},
  645. {0,&nofinalcr,"remove CR marker in all lines in region"},
  646. {0,&linefeed,"insert LF without CR"}};
  647. static menuline*crlf=_crlf;
  648. static menuline _del[]={{13,0,0},
  649. {0,&backspace,"delete char \033"},
  650. {0,&delet,"delete char \032"},
  651. {0,&killwordb,"kill word \033"},
  652. {0,&killwordf,"kill word \032"},
  653. {0,&killsent,"kill sentence \032"},
  654. {0,&kill_to_eol,"kill (to) EOL"},
  655. {0,&killregion,"kill region"},
  656. {0,©kill,"copy region to kill ring"},
  657. {0,&delblanklines,"delete blank lines round cursor"},
  658. {0,&deletewhite,"delete all spaces & tabs round cursor"},
  659. {0,&leaveonewhite,"delete blanks, leave 1 space"},
  660. {0,&replyank,"replace yank by previous kill"},
  661. {0,&yank,"yank last kill"}};
  662. static menuline*del=_del;
  663. static menuline _insert[]={{6,0,0},
  664. {0,&insertspaces,"insert space"},
  665. {0,&literalchar,"insert any char literally"},
  666. {0,&newline,"insert EOL"},
  667. {0,&openline,"insert EOL after cursor"},
  668. {0,&insert,"insert text"},
  669. {0,&overlay,"overlay text"}};
  670. static menuline*insert=_insert;
  671. static menuline _macro[]={{8,0,0},
  672. {0,&beginmacro,"start recording macro"},
  673. {0,&endmacro,"stop recording macro"},
  674. {0,&namemacro,"give macro a name"},
  675. {0,&obey,"obey current macro"},
  676. {0,¯omenu,"menu of current macros"},
  677. {0,&prmacro,"insert copy of current macro"},
  678. {0,&prmacros,"insert copy of all macros"},
  679. {0,&readmacros,"read macros & obey buffer bindings from a buffer"}};
  680. static menuline*macro=_macro;
  681. static menuline _mark[]={{7,0,0},
  682. {0,&popmark,"popmark"},
  683. {0,&pushmark,"pushmark"},
  684. {0,&remmark,"remove top mark from stack"},
  685. {0,&setmark,"set mark"},
  686. {0,&swopmark,"swop mark with cursor"},
  687. {0,&markbof,"set mark to start of buffer"},
  688. {0,&markeof,"set mark to end of buffer"}};
  689. static menuline*mark=_mark;
  690. static menuline _misc[]={{20,0,0},
  691. {0,&refresh_display,"cursor line becomes Nth/middle of screen"},
  692. {0,&prkeynames,"insert list of names of all alt and special keys"},
  693. {0,&prsubrnames,"insert list of all subroutine names"},
  694. {0,&accentedletters,"stop/start treating PC accented letters as alphanumeric"},
  695. {0,&calldos,"call DOS command"},
  696. {0,&cccmode,"whether or not to counterchange at cursor"},
  697. {0,&checkhelp,"insert report re any bound keys not listed in file HELP"},
  698. {0,&dis_play,"display text on the mode line in cyan"},
  699. {0,&displayhex,"display from cursor as hexadecimal"},
  700. {0,&movetext,"move (text between mark N and mark N+1) to cursor"},
  701. {0,©text,"copy (text between mark N and mark N+1) to cursor"},
  702. {0,&repeat,"obey last instruction again"},
  703. {0,&setrightmargin,"find/set right margin for auto eol"},
  704. {0,&setsortcol,"find/set left margin for sortlines"},
  705. {0,&showregion,"show region, or from mark 1 to mark 2"},
  706. {0,&sortlines,"sort lines in region into alphabetical order"},
  707. {0,×4,"multiply current N by 4"},
  708. {0,&version,"display date of this version of AAEMACS"},
  709. {0,&whoa,"discard arg typed"},
  710. {0,&menu,"this subroutine (menu of subroutine descriptions)"}};
  711. static menuline*misc=_misc;
  712. static menuline _move[]={{20,0,0},
  713. {0,&goleft,"\033 a char"},
  714. {0,&goright,"\032 a char"},
  715. {0,&gotocol,"go to Nth char in line"},
  716. {0,&gouppart,"\030 a line-fold"},
  717. {0,&godownpart,"\031 a line-fold"},
  718. {0,&goup,"\030 a line"},
  719. {0,&godown,"\031 a line"},
  720. {0,&gotobol,"go to start of line"},
  721. {0,&gotoeol,"go to end of line"},
  722. {0,&gotoline,"go to line N"},
  723. {0,&skipwordb,"\033 a word"},
  724. {0,&skipwordf,"\032 a word"},
  725. {0,&skipsentb,"\033 a sentence"},
  726. {0,&skipsentf,"\032 a sentence"},
  727. {0,&skipparab,"\030 a paragraph"},
  728. {0,&skipparaf,"\031 a paragraph"},
  729. {0,&page_down,"\031 a screenful"},
  730. {0,&page_up,"\030 a screenful"},
  731. {0,&gotobof,"go to top of buffer"},
  732. {0,&gotoeof,"go to end of buffer"}};
  733. static menuline*move=_move;
  734. static menuline _reformat[]={{3,0,0},
  735. {0,&formatpara,"reformat paragraph"},
  736. {0,&formatregion,"reformat region"},
  737. {0,&formatinsetregion,"reformat region and keep its left inset chars"}};
  738. static menuline*reformat=_reformat;
  739. static menuline _Repl[]={{4,0,0},
  740. {0,&repl,"replace"},
  741. {0,&replask,"replace, asking"},
  742. {0,&replword,"replace, whole words only"},
  743. {0,&replwordask,"replace, whole words only, asking"}};
  744. static menuline*Repl=_Repl;
  745. static menuline _Search[]={{6,0,0},
  746. {0,&findb,"search \033"},
  747. {0,&findf,"search \032"},
  748. {0,&findwordb,"search \033, whole word only"},
  749. {0,&findwordf,"search \032, whole word only"},
  750. {0,&incrfindb,"incremental search \033"},
  751. {0,&incrfindf,"incremental search \032"}};
  752. static menuline*Search=_Search;
  753. static menuline _spelling[]={{4,0,0},
  754. {0,&checkspelling,"check spelling of next word or all region"},
  755. {0,&emptyappendix,"empty the dictionary's appendix"},
  756. {0,&getappendix,"load dictionary's appendix from region"},
  757. {0,&putappendix,"copy dictionary's appendix into a buffer"}};
  758. static menuline*spelling=_spelling;
  759. static menuline _tab[]={{3,0,0},
  760. {0,&expandtabs,"expand all tabs to spaces in region"},
  761. {0,&maketabs,"convert spaces to tabs in region"},
  762. {0,&tabulate,"insert or overlay tab char"}};
  763. static menuline*tab=_tab;
  764. static menuline _Twiddle[]={{3,0,0},
  765. {0,&twiddle,"twiddle: move this char \032"},
  766. {0,&twiddlewords,"twiddle: move this word \032"},
  767. {0,&twiddlelines,"twiddle: move this line \031"}};
  768. static menuline*Twiddle=_Twiddle;
  769. static menuline _windows[]={{4,0,0},
  770. {0,&splitwindow,"split this window"},
  771. {0,&onewindow,"back to one-window mode"},
  772. {0,&nextwindow,"go to next window in ring of windows"},
  773. {0,&prevwindow,"go to previous window in ring of windows"}};
  774. static menuline*windows=_windows;
  775. static menuline _topmenu[]={{21,0,0},
  776. {1,(subr*)bindmenu,"KEY BINDINGS"},
  777. {1,(subr*)buf,"FILES AND BUFFERS"},
  778. {1,(subr*)c,"C LANGUAGE FEATURES"},
  779. {0,&callsubr,"call subr or macro"},
  780. {1,(subr*)Case,"CASE OF LETTERS"},
  781. {1,(subr*)del,"DELETING"},
  782. {0,&help,"get help"},
  783. {1,(subr*)insert,"INSERTING"},
  784. {1,(subr*)macro,"MACROS"},
  785. {1,(subr*)mark,"MARKS"},
  786. {1,(subr*)misc,"MISCELLANEOUS"},
  787. {0,&modemenu,"find & reset buffer's modes by a menu"},
  788. {1,(subr*)move,"MOVE CURSOR"},
  789. {1,(subr*)Twiddle,"MOVING A CHAR/WORD/LINE"},
  790. {1,(subr*)reformat,"REFORMAT TEXT"},
  791. {1,(subr*)Repl,"REPLACING"},
  792. {1,(subr*)Search,"SEARCHING"},
  793. {1,(subr*)spelling,"SPELLING CHECKER"},
  794. {1,(subr*)tab,"TABULATING"},
  795. {1,(subr*)windows,"SPLIT SCREEN MODE"},
  796. {0,&finish,"exit from AAEMACS"}};
  797. static menuline*topmenu=_topmenu,*M[4];
  798. if(Jerry.mc) {char*MN[]={"Move","Copy","Delete","Exit","mainmenU"};
  799.     MK=B->Mark(1); (MK-B->Mark(2)).color(White,Magenta); B->display();
  800.     B->dotcc=B->dotcc2=-1; /* stop dotty() from playing with the cursor */
  801.     switch(bottommenu("",4,"MCDEU",MN)) {
  802.     case 0: movetext(); return;
  803.     case 1: copytext(); return;
  804.     case 2: (B->Mark(1)-B->Mark(2)).kill(); return;
  805.     case 3: return;
  806.     case 4:;}}
  807. char mp[4]; mp[0]=0; M[0]=&topmenu[0]; int Z=0,m=0; val A(1,0); mousestate MS;
  808. MS=Jerry; str_cpy(scrsave,screen,ss); thisstep.f=kf(_idle);
  809. display("\030\031\033\032 to move pointer, RET to choose, END to back out,\
  810.  ctrl_ for help",gp_Rows-1,0,Cyan); Jerry.bd=0;
  811. MENU: Jerry.mc=1; n=M[m][0].submenu; Jerry.range(n,80); Jerry.move(k=mp[m],0);
  812. for(i=n*gp_Cols-1;i>=0;i--) screen[i]=256*White;
  813. for(i=0;i<n;i++) {p=&screen[i*gp_Cols+1];
  814.     j=1; s=M[m][i+1].text; while(*s) *p++=(*s++)+256*Green;}
  815. LINE: screen[k*gp_Cols]+=2;
  816. CHAR: j=k; if(Z!=-mousemove) Jerry.move(k,0); switch(Z=getkey()){
  817. case -mousemove: k=Jerry.y; break;
  818. case -lbutton: goto CHOSEN;
  819. case -uparrow: k--; break; case -downarrow: k++; break;
  820. case -rightarrow: k+=5; break; case -leftarrow: k-=5; break;
  821. case CR: goto CHOSEN;
  822. case -end: case -mbutton: case -rbutton: Jerry.mc=0; goto BACKOUT;
  823. case '_'-64: if(M[m][k+1].submenu) {beep(); goto CHAR;}
  824.     s=kf(M[m][k+1].sub).Subrname(); if(!(z=Subrhelp(s))) {beep(); goto CHAR;}
  825.     j=0; display(*z++,j++,0,Orange);
  826.     while(*z?**z!='{':0) display(*z++,j++,0,Orange); getkey(); goto MENU;
  827. default: goto CHAR;}
  828. while(k<0) k+=n; while(k>=n) k-=n; mp[m]=k;
  829. if(j!=k) {screen[j*gp_Cols]&=0xff00; screen[k*gp_Cols]+=2;} goto CHAR;
  830. BACKOUT: str_cpy(screen,scrsave,ss); if(m--) goto MENU; Jerry=MS; return;
  831. CHOSEN: sb=M[m][k+1].sub;
  832. if(M[m][k+1].submenu) if(m>=3) {MOAN("BUG: menus too deep");}
  833. else{mp[m]=k;M[++m]=(menuline*)sb;str_cpy(screen,scrsave,ss);mp[m]=0;goto MENU;}
  834. p=r=screen+(gp_Rows-1)*gp_Cols; for(i=0;i<gp_Cols;i++) p[i]=256*Orange;
  835. s=kf(sb).Subrname(); while(*s) (*p++)+=*s++; Jerry.mc=0;
  836. s=": numerical argument?"; while(*s) (*p++)+=*s++; q=p+1; u=1; v=0;
  837. while(1) switch(i=getkey()) {
  838. case -pagedown: case -lbutton: goto Z;
  839. case '0'...'9': if(q<p+30) {v=v*10+i-'0'; *q++=i+256*Cyan;} else beep(); break;
  840. case '+': u=1; *p=256*Cyan; break;
  841. case '-': u=-1; *p='-'+256*Cyan; break;
  842. case 8: if(q>p+1) {*--q=256*Cyan; v/=10;} break;
  843. case -end: case -mbutton: case -rbutton: str_cpy(screen,scrsave,ss); goto MENU;
  844. default: beep();}
  845. Z: if(q>p+1) A=val(u*v); str_cpy(screen,scrsave,ss);
  846. thisstep.f=kf(sb); thisstep.N=A; sb(A,val(0,0),val(0,0)); Jerry=MS;}
  847.